/*
 * CAD System for seamlessly constructing 2D and 3D scenes with aspects and motion
 * Copyright (C) 2013 Bernhard Haumacher and others
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.haumacher.draw5d.dataflow.core;

import java.util.ArrayList;
import java.util.List;

import de.haumacher.draw5d.dataflow.Block;
import de.haumacher.draw5d.dataflow.Observable;
import de.haumacher.draw5d.dataflow.Scope;

/**
 * Default {@link Scope} implementation.
 * 
 * @author <a href="mailto:haui@haumacher.de">Bernhard Haumacher</a>
 * @version $Revision: 21 $ $Author: haumacher@googlemail.com $ $Date: 2013-07-27 18:49:19 +0000 (Sat, 27 Jul 2013) $
 */
public class ScopeImpl implements Scope {
	
	private static final int PHASE_REVALIDATE = 0;

	private static final int PHASE_INVALID = 1;
	
	private static final int PHASE_IMPACT_ANALYSIS = 2;
	
	private final List<Observable> _invalids = new ArrayList<Observable>();

	private Block<Observable> revalidator = new Block<Observable>() {

		@Override
		public void apply(Observable invalid) {
			if (invalid.mark(PHASE_REVALIDATE) == PHASE_REVALIDATE) {
				// Already considered.
				return;
			}

			invalid.foreachDependency(this);
			invalid.validate();
		}

	};

	@Override
	public void validate() {
		List<Observable> impact = new ArrayList<Observable>();
		for (Observable invalid : _invalids) {
			fillImpact(impact, invalid);
		}
		
		_invalids.clear();
		
		for (Observable invalid : impact) {
			revalidator.apply(invalid);
		}
		
		assert isValid() : "Validation of " + impact + " invalidates " + _invalids;
	}

	private void fillImpact(List<Observable> impact, Observable invalid) {
		if (invalid.mark(PHASE_IMPACT_ANALYSIS) == PHASE_IMPACT_ANALYSIS) {
			// Already considered.
			return;
		}
		
		impact.add(invalid);
		for (Observable usage : invalid.getUsage()) {
			fillImpact(impact, usage);
		}
	}

	@Override
	public boolean isValid() {
		return _invalids.isEmpty();
	}

	@Override
	public void registerInvalid(Observable invalid) {
		int mark = invalid.getMark();
		if (mark == PHASE_REVALIDATE) {
			invalid.mark(PHASE_INVALID);
			_invalids.add(invalid);
		}
	}

}
